/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.uca.dss.curso1011.grupo3.gestionreservasextendido;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import org.uca.dss.curso1011.grupo3.gestionreservas.ListadoDisponibilidad;
import org.uca.dss.curso1011.grupo3.gestionreservas.Trayecto;
import org.uca.dss.curso1011.grupo3.interfazextendido.InterfazListados;

/**
 * Esta clase implementa a la interfaz InterfazListados necesaria para tener toda la funcionalidad
 * de los listados de disponibilidad para realizar las consiguientes reservas.
 *
 *@author Adolfo Barroso, Levi Orta, Juan Manuel Chaves
 */
public class ListadoDisponibilidadExtendido extends ListadoDisponibilidad implements InterfazListados {

    /**
     * Constructor de la clase ListadoDisponibilidadExtendido. Esta constructor generara un listado de disponibilidad
     * extendido para dar la posibilidad de viajes con transbordos. Recibe como parametros el patch donde se encuentra
     * el fichero con los datos de los trenes disponibles, asi como tambien el de los trayectos disponibles. Este constructor
     * necesita estos parametros para inicializar la clase base de la que hereda.
     *
     * @param ficheroTrenes, String que almacena el patch con los datos de los trenes.
     * @param ficheroTrayecto, String que almacena el patch con los datos de los trayectos.
     */
    public ListadoDisponibilidadExtendido(String ficheroTrenes, String ficheroTrayecto) throws FileNotFoundException, IOException {
        super(ficheroTrenes, ficheroTrayecto);
    }

    /**
     * Permite obtener los itinerarios disponibles para cubrir un trayecto entre
     * una cidad origen y una ciudad destino. Estos itinerarios tambien seran filtrados
     * por una fecha que se realiza el viaje y por una hora de salida y una hora maxima de llegada.
     *
     * @param origen ciudad origen
     * @param destino ciudad destino
     * @param fecha fecha de inicio viaje
     * @param horaSalida indica la hora a partir del cual el viaje puede empezar
     * @param horaLlegada indica la hora máxima de llegada
     * @return un array con los itinerarios posibles
     */
    @Override
    public List<Itinerario> getItinerariosEntre(String origen, String destino, LocalDate fechaSalida, LocalTime horaSalida, LocalTime horaLlegada) {
    
        List<Itinerario> listaItinerariosDiario = this.getItinerarios(origen, destino, fechaSalida);


        for(int i = 0; i < listaItinerariosDiario.size();)
        {
            Itinerario datosItinerario = listaItinerariosDiario.get(i);

            //filtro los itinerarios directos
            if(datosItinerario.size() == 1)
            {
                InformacionTrayecto datosTrayecto = datosItinerario.get(0);

                if(datosTrayecto.getHoraSalida().isBefore(horaSalida) || datosTrayecto.getHoraLlegada().isAfter(horaLlegada))
                {
                    listaItinerariosDiario.remove(i);
                }
                else
                    ++i;
            }
            else
            {
                InformacionTrayecto datosPrimerTrayecto = datosItinerario.get(0);
                InformacionTrayecto datosSegundoTrayecto = datosItinerario.get(1);

                if( datosPrimerTrayecto.getHoraSalida().isBefore(horaSalida))
                {
                    listaItinerariosDiario.remove(i);
                }
                else if(datosSegundoTrayecto.getHoraLlegada().isAfter(horaLlegada))
                {
                    listaItinerariosDiario.remove(i);

                }
                else if(datosSegundoTrayecto.getHoraSalida().isBefore(datosPrimerTrayecto.getHoraLlegada().plusMinutes(10)))
                {
                    listaItinerariosDiario.remove(i);
                }
                else
                    ++i;

            }
        }

        return listaItinerariosDiario;
}


    /**
     * Permite obtener los itinerarios disponibles para cubrir un trayecto entre
     * una cidad origen y una ciudad destino en una fecha concreta. No se tiene en cuenta
     * horarios.
     *
     * @param origen ciudad origen
     * @param destino ciudad destino
     * @param fecha fecha de inicio viaje
     * @return un array con los itinerarios posibles
     */
    @Override
    public List<Itinerario> getItinerarios(String origen, String destino, LocalDate fechaSalida) {

        //la lista de intinerarios a devolver
        List<Itinerario> listaItinerarios = new ArrayList<Itinerario>();

        //----------------------------------------------------------------------
        //si existe un trayecto directo entre origen y destino, lo metemos
        List<Trayecto> trayectosDirectos = this.gestionReservas.getTrayectos(origen, destino);

        List<Itinerario> listaItinerariosDirectos = new ArrayList<Itinerario>();
        //Crearemos los itinerarios directos
        for(int i = 0; i < trayectosDirectos.size(); ++i)
        {
            Trayecto trayectoDirecto = trayectosDirectos.get(i);
            listaItinerariosDirectos = getItinerariosTrayecto(trayectoDirecto, fechaSalida);

            for(int j = 0; j < listaItinerariosDirectos.size(); j++){
                listaItinerarios.add(listaItinerariosDirectos.get(j));
            }
        }

        //----------------------------------------------------------------------
        //obtener la lista de trayectos que salen desde el origen
        List<Trayecto> trayectosOrigen = this.gestionReservas.getTrayectosDesde(origen);

        //obtener la lista de trayectos que van hacia el destino
        List<Trayecto> trayectosDestino = this.gestionReservas.getTrayectosHasta(destino);

        //recorremos todos los trayectos que salen del origen
        for(int itTrayOrig = 0; itTrayOrig < trayectosOrigen.size(); ++itTrayOrig)
        {
            //recorremos todos los trayectos que llegan al destino
            for(int itTrayDest = 0; itTrayDest < trayectosDestino.size(); ++itTrayDest)
            {
                Trayecto trayectoPrimero = trayectosOrigen.get(itTrayOrig);
                Trayecto trayectoSegundo = trayectosDestino.get(itTrayDest);

                //si el destino del primer trayecto coincide con el origen del segundo trayecto (1 transbordo)
                if(trayectoPrimero.getDestino().equals(trayectoSegundo.getOrigen()))
                {
                    List<Itinerario> listaItinerariosIndirectos = new ArrayList<Itinerario>();
                    listaItinerariosIndirectos = getItinerariosTrayecto(trayectoPrimero, trayectoSegundo, fechaSalida);
                    
                    for(int j = 0; j < listaItinerariosIndirectos.size(); j++)
                        listaItinerarios.add(listaItinerariosIndirectos.get(j));
                }
            }
        }
        return listaItinerarios;
    }

    /**
     * Se obtiene la lista de itinerarios que cubren un trayecto concreto pasado como parametro y en una
     * fecha concreta.
     *
     * @param trayecto, trayecto para el cual queremos obtener la lista de itinerarios que lo cumplen
     * @param fechaSalida fecha de inicio viaje
     * @return un array con los itinerarios posibles
     */
    private List<Itinerario> getItinerariosTrayecto(Trayecto trayecto, LocalDate fechaSalida){

        //la lista de intinerarios a devolver
        List<Itinerario> listaItinerarios = new ArrayList<Itinerario>();

        //Nos quedamoa solo con los horarios en los que hay sitio libre
        Map<LocalTime,LocalTime> horariosSalidaLlegada = this.getHorariosTrayecto(trayecto, fechaSalida);

        Iterator it = horariosSalidaLlegada.entrySet().iterator();
        while (it.hasNext())
        {
            Itinerario itinerario = new ImplementacionItinerario();
            Map.Entry e = (Map.Entry)it.next();
            InformacionTrayecto informaciontrayecto = new InformacionTrayecto(trayecto.getOrigen(), trayecto.getDestino(), new LocalTime(e.getKey()), new LocalTime(e.getValue()), trayecto.getPrecio());
            itinerario.add(informaciontrayecto);
            listaItinerarios.add(itinerario);
        }
        return listaItinerarios;
    }

    /**
     * Se obtiene la lista de itinerarios que cubren dos trayectos pasado como parametros, para una fecha concreta
     *
     *
     * @param trayectoPrimero, trayecto para el cual queremos obtener la lista de itinerarios que lo cumplen
     * @param trayectoSegundo, fecha de inicio viaje
     * @param fechaSalida fecha de inicio viaje.
     * @return un array con los itinerarios posibles
     */
    private List<Itinerario> getItinerariosTrayecto(Trayecto trayectoPrimero, Trayecto trayectoSegundo, LocalDate fechaSalida){

        //la lista de intinerarios a devolver
        List<Itinerario> listaItinerarios = new ArrayList<Itinerario>();

        //Nos quedamoa solo con los horarios en los que hay sitio libre del primer trayecto
        Map<LocalTime,LocalTime> horariosSalidaLlegadaPrimero = this.getHorariosTrayecto(trayectoPrimero, fechaSalida);
        //Nos quedamoa solo con los horarios en los que hay sitio libre del segundo trayecto
        Map<LocalTime,LocalTime> horariosSalidaLlegadaSegundo = this.getHorariosTrayecto(trayectoSegundo, fechaSalida);

        //recorremos todos los horarios del primer itinerario
        Iterator itPrimero = horariosSalidaLlegadaPrimero.entrySet().iterator();
        while (itPrimero.hasNext())
        {
            Map.Entry ePrimero = (Map.Entry)itPrimero.next();

            //creamos informaciontrayecto del primer trayecto
            InformacionTrayecto informaciontrayectoPrimero = new InformacionTrayecto(trayectoPrimero.getOrigen(), trayectoPrimero.getDestino(), new LocalTime(ePrimero.getKey()), new LocalTime(ePrimero.getValue()), trayectoPrimero.getPrecio());

            //recorremos todos los horarios del segundo itinerario
            Iterator itSegundo = horariosSalidaLlegadaSegundo.entrySet().iterator();
            while (itSegundo.hasNext())
            {
                Map.Entry eSegundo = (Map.Entry)itSegundo.next();

                //creamos informaciontrayecto del segundo trayecto
                InformacionTrayecto informaciontrayectoSegundo = new InformacionTrayecto(trayectoSegundo.getOrigen(), trayectoSegundo.getDestino(), new LocalTime(eSegundo.getKey()), new LocalTime(eSegundo.getValue()), trayectoSegundo.getPrecio());

                Itinerario itinerario = new ImplementacionItinerario();
                //creamos el itinerario
                itinerario.add(informaciontrayectoPrimero);
                itinerario.add(informaciontrayectoSegundo);
                
                //anadimos el itinerario al la lista de itinerarios final
                listaItinerarios.add(itinerario);
            }
        }
        return listaItinerarios;
    }
}
